package org.example.big_data.controller;

import jakarta.validation.constraints.Pattern;
import org.example.big_data.pojo.Result;
import org.example.big_data.pojo.User;
import org.example.big_data.service.UserService;
import org.example.big_data.utils.JwtUtil;
import org.example.big_data.utils.Md5Util;
import org.example.big_data.utils.ThreadLocalUtil;
import org.hibernate.validator.constraints.URL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/user")
@Validated
public class UserController {
    @Autowired
    UserService userService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;  //reids操作模板类

    @PostMapping("/register")  //注册功能接口
    public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password) {  //实战：参数校验框架validation使用:参数只是正则表达式
        User user = userService.findByUserName(username);
        if (user != null) {
            return Result.error("用户名已存在");
        }
        else {
            userService.register(username, password);
            return Result.success();
        }

    }

    /**
     *使用Redis更新登录功能接口
     * 获取存入载荷后的token，存入Redis中
     */
    @PostMapping("/login")  //登录功能接口
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username,@Pattern(regexp = "^\\S{5,16}$")String password) {
        User loginuser = userService.findByUserName(username);  //loginuser是查询出的用户对象
        if (loginuser == null) {
            return Result.error("用户不存在请注册");
        }
        else {
            String md5String = Md5Util.getMD5String(password);  //加密
            if (md5String.equals(loginuser.getPassword())) {  //加密的密码和数据库中的密码比较
                //登陆成功,调用jwt工具类生成token的方法，传入一个Map封装已登录用户(loginuser)的id和用户名
                Map<String,Object> claims = new HashMap<>();
                claims.put("id",loginuser.getId());
                claims.put("username",loginuser.getUsername());

                String token = JwtUtil.genToken(claims);  //得到一个token，其中的载荷："claims": {"id": 当前用户id值, "username": "当前用户名"}
                //存入Redis中,
                ValueOperations<String,String> operations = stringRedisTemplate.opsForValue();
                operations.set(token,token,1, TimeUnit.DAYS);  //一天失效

                return Result.success("登陆成功",token);

            }
            else {
                return Result.error("密码错误");
            }
        }
    }

    //2.10  /user/userinfo获取用户信息的功能接口，接收HTTP请求，获取请求头中的token等信息参与查询数据库，返回用户信息

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    @GetMapping("/userinfo")
    public Result<User> userInfo(/*@RequestHeader(name= "Authorization") String token*/) {//2.11  使用threadLocal优化代码不再手动接收token参数并解析token，而是直接从线程对象中获取想要的值
//        Map<String,Object> map =  JwtUtil.parseToken(token);
        //获取一下username
//        String username = (String) map.get("username");  //强转一下，因为map.get("username")返回的是Object类型

        Map<String,Object> claim = ThreadLocalUtil.get();  //获取线程变量中存的claims集合
        String username = (String) claim.get("username");
        User user = userService.findByUserName(username);
        return Result.success(user);
    }

    //2.12  /user/update更新用户数据接口
    //@RequestBody注解：用于将HTTP请求体中的JSON数据转化为user对象
    //@Validated  使POJO类上的限制注解(如@NotNull)生效
    @PutMapping("/update")
    public Result update(@RequestBody @Validated User user) {
        userService.update(user);
        return Result.success("更改成功小馋猫");
    }

    //2.13    更新头像/user/updateAvatar接口,@URL注解：用于校验请求参数是否是一个合法的URL
    @PatchMapping("/updateAvatar")
    public Result updateAvatar(@RequestParam @URL String avatarUrl) {
        userService.updateAvatar(avatarUrl);
        return Result.success();
    }


    /**
     *redis更新逻辑：
     * 更新密码后，删除redis中的token，然后重新生成一个token存入redis中，根据原token来删除，怎么获取原token呢
     * 在访问接口的时候，前端都会在请求头中携带一个Authorization字段，在处理方法的参数上获取@RequestHeader(name= "Authorization") String token
     */
    //2.14    更新密码/user/updatePwd接口，请求参数是一个json数据且无法和POJO类中的属性相匹配的情况：在处理方法的参数上声明一个Map集合来接收参数+@RequestBody注解将请求体中的json转化为Map集合
    @PatchMapping("/updatePwd")
    public Result updatePwd(@RequestBody Map<String,String> params,@RequestHeader(name= "Authorization") String token) {
        //1 校验参数
        String oldPwd = params.get("old_pwd");  //请求参数中的旧密码
        String newPwd = params.get("new_pwd");  //请求参数中的新密码
        String rePwd = params.get("re_pwd");  //请求参数中的确认密码
        //StringUtils.hasLength()方法判断字符串是否为空或null，不为空或null返回true,
        if(!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd) || !StringUtils.hasLength(rePwd)) { //只要有一个为空或null
            return Result.error("请输入完整的密码");
        }
        //1.2 校验旧密码是否正确，从token中获取已登录用户名，然后查询一行数据，取出查询出的user对象的密码属性
        Map<String,Object> claim = ThreadLocalUtil.get();  //获取线程变量中存的claims集合，工具类提供get方法
        String username = (String) claim.get("username");  //获取用户名
        User logineduser = userService.findByUserName(username);
        //1.3 校验新密码和确认密码是否一致，细节，数据库中查出的密码是加密后的，前端传入的HTTP请求参数中的密码是未加密的，所以需要先加密再比较，工具类提供加密方法
        String md5String = Md5Util.getMD5String(oldPwd);
        if (!md5String.equals(logineduser.getPassword())) {
            return Result.error("旧密码错误，请重新输入");
        }
        //1.4 校验新密码和旧密码是否一致，不用加密
        if (oldPwd.equals(newPwd)) {
            return Result.error("新密码不能和旧密码一致");
        }
        //1.5 校验新密码和确认密码是否一致，不用加密
        if (!newPwd.equals(rePwd)) {
            return Result.error("新密码和确认密码不一致");
        }

        //2，校验通过后，调用service层方法
        userService.updatePwd(newPwd);

        /**
         * 3，更新密码后，删除redis中的token,需调用：operations.getOperations().delete
         */
        ValueOperations<String,String> operations = stringRedisTemplate.opsForValue();
        operations.getOperations().delete(token);


        return Result.success(null,"更新成功，请重新登录！");
    }

}